﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace AnaxibiaStudios.Smug.Advanced
{
  public class ProxyRepository
  {
    private static ProxyRepository _repo = new ProxyRepository();

    public static object CreateInstance(Type targetType, ProxyConfiguration config, params object[] args)
    {
      var proxyType = LoadProxyType(targetType);
      return _repo.Create(proxyType, config, args);
    }

    public static Type LoadProxyType(Type targetType)
    {
      if (!_repo.Contains(targetType))
      {
        lock (_repo)
        {
          if (!_repo.Contains(targetType))
          {
            return _repo.Build(targetType);
          }
        }
      }

      return _repo._typeCache[targetType];
    }

    public Dictionary<Type, Type> _typeCache;
    protected Internal.Compiler _compiler;

    public ProxyRepository()
    {
      _typeCache = new Dictionary<Type, Type>();
      _compiler = new Internal.Compiler();
    }

    public bool Contains(Type type)
    {
      return _typeCache.ContainsKey(type);
    }

    public Type Build<TTargetType>()
    {
      return Build(typeof(TTargetType));
    }

    public Type Build(Type targetType)
    {
      var builder = new Internal.SourceBuilder(targetType);
      builder.GenerateSource();

      var proxyType = _compiler.Compile(targetType, builder.Source, builder.ReferencedAssemblies, Internal.Constants.DotNet4_0);

      _typeCache[targetType] = proxyType;
      return proxyType;
    }

    public object Create(Type proxyType, ProxyConfiguration config, params object[] args)
    {
      var largs = new List<object>();
      largs.Add(config);
      largs.AddRange(args);

      var cargs = largs.ToArray();

      var argTypes = Type.GetTypeArray(cargs);

      //string fullname = string.Format("{0}.{1}", TargetNamespace, ClassName);
      var ctor = proxyType.GetConstructor(Internal.Constants.Flags, null, argTypes, null);

      if (ctor == null)
      {
        throw new SmugConstructorNotFoundException(proxyType);
      }

      if (config.UseRealMethodsInConstructor)
        Internal.InvocationHandler.SetRealCtorCallsOverride(true);

      var inst = ctor.Invoke(cargs);
      Internal.InvocationHandler.SetRealCtorCallsOverride(false);

      return inst;
    }

    public static IProxy CastProxy(object proxy)
    {
      return proxy as IProxy;
    }
  }
}
